 /* The file is part of the Waff web framework.
 *
 * Copyright (c) 2011, Grzegorz Skorupa
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditionsare met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list of conditions
 *   and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 *   and the following disclaimer in the documentation and/or other materials provided with the distribution.
 * - Neither the name of the Grzegorz Skorupa nor the names of its contributors may be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package pl.org.waff;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public final class MainFilter implements Filter {

    private FilterConfig filterConfig;
    private String mobileHandling = null;
    private static String standardDomain = null;
    private static String mobileDomain = null;
    private static String standardJspPrefix = null;
    private static String mobileJspPrefix = null;
    private static String applicationPrefix = null;
    private static String testMobileAgent = null;
    public static boolean modifyDomain = false;
    private static boolean attributeOnly = false;
    private static final Logger log = Logger.getLogger(MainFilter.class.getName());

    
    //TODO: MultiSwitch.setMobileHandling("n") - how to force mobile layout after clicking some link
    /* All requested URL should be ended with '.do'
     * If not, this filter appends '_.do' to the request path and redirects.
     */
    @Override
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        String oryginalPath = request.getContextPath() + request.getServletPath();
        boolean pathToFile = oryginalPath.lastIndexOf(".") >= 0;
        boolean pathToAuth = oryginalPath.startsWith("/_ah");
        // we don't modify URLs to files
        if (!(pathToFile || pathToAuth)) {
            pl.org.waff.MultiSwitch ms = (pl.org.waff.MultiSwitch) request.getSession().getAttribute(WaffServlet.SWITCH_ATTRIBUTE_NAME);
            String mobileHandlingSwitch = null;
            if (ms != null) {
                mobileHandlingSwitch = ms.getMobileHandling();
            }
            // redirect to new URL if necessary
            if (mobileHandling != null) {
                if (!"n".equals(mobileHandlingSwitch)) {
                    if (handleMobileDevice(ms, oryginalPath, request, response)) {
                        return;
                    }
                }
            }
        }
        // if no object is targetted in URL then redirect to dummy '_' object (see WaffServlet)
        String path = oryginalPath;
        log.fine("PATH (filter):" + path);
        boolean forward = false;
        if (!(pathToFile || pathToAuth)) {
            if (path.endsWith("/")) {
                path = path + "_.do";
            } else {
                path = path + "/_.do";
            }
            forward = true;
        }
        request.setAttribute(WaffServlet.PATH_ATTRIBUTE_NAME, oryginalPath); // Navigator uses this attribute
        //response.setHeader(WaffServlet.PATH_ATTRIBUTE_NAME, oryginalPath);
        if (forward) {
            response.sendRedirect(path);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        log.fine("INIT (filter):");
        mobileHandling = config.getInitParameter("mobilehandling");
        standardDomain = config.getInitParameter("standardsubdomain");
        mobileDomain = config.getInitParameter("mobilesubdomain");
        standardJspPrefix = config.getInitParameter("standardjspprefix");
        mobileJspPrefix = config.getInitParameter("mobilejspprefix"); 
        applicationPrefix = config.getInitParameter("applicationprefix");
        testMobileAgent = config.getInitParameter("testmobileagent");
        
        if (applicationPrefix == null) {
            applicationPrefix = "";
        } else if (!applicationPrefix.startsWith("/")) {
            applicationPrefix = "/" + applicationPrefix;
        }
        if (mobileHandling != null) {
            if ("domain".equals(mobileHandling)) {
                modifyDomain = true;
                if (standardDomain == null || mobileDomain == null) {
                    mobileHandling = null;
                    //TODO: error handling
                }
            } else if ("jsp".equals(mobileHandling)) {
                if (standardJspPrefix == null || mobileJspPrefix == null) {
                    mobileHandling = null;
                    //TODO: error handling
                } else {
                    if (!mobileJspPrefix.endsWith("/")) {
                        mobileJspPrefix = mobileJspPrefix + "/";
                    }
                    if (!standardJspPrefix.endsWith("/")) {
                        standardJspPrefix = standardJspPrefix + "/";
                    }
                }
            } else if ("request".equals(mobileHandling)) {
                attributeOnly = true;
            } else {
                mobileHandling = null;
            }
        }
        log.fine("INIT (filter)mh:" + mobileHandling);
        //props.setProperty("mobileDomain", mobileDomain);
        //props.setProperty("standardDomain", standardDomain);
        //props.setProperty("applicationPrefix", applicationPrefix);
        //props.setProperty("testMobileAgent", testMobileAgent);
        //props.setProperty("standardJspPrefix", standardJspPrefix);
        //props.setProperty("mobileJspPrefix", mobileJspPrefix);
        //props.setProperty("mobileHandling", mobileHandling);
    }

    private boolean handleMobileDevice(MultiSwitch ms, String oryginalPath, HttpServletRequest req, HttpServletResponse resp)
            throws IOException, ServletException {
        int port = req.getServerPort();
        String ret;
        String domainName = req.getServerName();
        String mainDomain;
        String mainPath;
        boolean isStandard = false;
        boolean isMobile = false;
        // check if device is a mobile device
        String deviceName = req.getHeader("User-Agent");
        boolean isMobileDevice = false;

        if (null != deviceName) {
            if(deviceName.indexOf("Android") >= 0){
                isMobileDevice=true;
                deviceName="Android";
            }else if(deviceName.indexOf("IPhone") >= 0){
                isMobileDevice=true;
                deviceName="iPhone";
            }else if(deviceName.indexOf("iPad") >= 0){
                isMobileDevice=true;
                deviceName="iPad";
            }else if (testMobileAgent != null && deviceName.indexOf(testMobileAgent) >= 0) {
                isMobileDevice = true;
                deviceName=testMobileAgent;
            }
        }
        if (isMobileDevice) {
            if (ms == null) {
                ms = new MultiSwitch();
            }
            ms.setMobileDevice(deviceName);
            req.getSession().setAttribute(WaffServlet.SWITCH_ATTRIBUTE_NAME, ms);
        }
        if (attributeOnly) {
            return false;
        }
        if (modifyDomain) {
            mainPath = oryginalPath;
            if (domainName.indexOf(mobileDomain) == 0) {
                mainDomain = domainName.substring(mobileDomain.length());
                isMobile = true;
            } else if (domainName.indexOf(standardDomain) == 0) {
                mainDomain = domainName.substring(standardDomain.length());
                isStandard = true;
            } else {
                mainDomain = domainName;
            }
        } else {
            mainDomain = domainName;
            int prefixLength;
            if (oryginalPath.indexOf(applicationPrefix + mobileJspPrefix) == 0) {
                prefixLength = applicationPrefix.length() + mobileJspPrefix.length();
                mainPath = oryginalPath.substring(prefixLength);
                isMobile = true;
            } else if (oryginalPath.indexOf(applicationPrefix + standardJspPrefix) == 0) {
                prefixLength = applicationPrefix.length() + standardJspPrefix.length();
                mainPath = oryginalPath.substring(prefixLength);
                isStandard = true;
            } else if (oryginalPath.indexOf(applicationPrefix) == 0) {
                prefixLength = applicationPrefix.length();
                mainPath = oryginalPath.substring(prefixLength);
            } else {
                mainPath = oryginalPath;
            }
        }

        if (log.isLoggable(Level.FINE)) {
            if (port == 80) {
                ret = req.getScheme() + "://" + domainName + oryginalPath;
            } else {
                ret = req.getScheme() + "://" + domainName + ":" + port + oryginalPath;
            }
            log.fine("HANDLEMOBILE:[" + ret + "]");
        }

        if (isMobileDevice) {
            if (isMobile) {
                return false;
            }
            if (modifyDomain) {
                domainName = mobileDomain + mainDomain;
            } else {
                //TODO:
                mainPath = applicationPrefix + mobileJspPrefix + mainPath;
                ((HttpServletRequest) req).getSession().setAttribute(WaffServlet.PATH_PREFIX_ATTRIBUTE_NAME, mobileJspPrefix);
            }
        } else {
            if (isStandard) {
                return false;
            }
            if (modifyDomain) {
                domainName = standardDomain + mainDomain;
            } else {
                //TODO:
                mainPath = applicationPrefix + standardJspPrefix + mainPath;
                ((HttpServletRequest) req).getSession().setAttribute(WaffServlet.PATH_PREFIX_ATTRIBUTE_NAME, standardJspPrefix);
            }
        }
        if (port == 80) {
            ret = req.getScheme() + "://" + domainName + mainPath;
        } else {
            ret = req.getScheme() + "://" + domainName + ":" + port + mainPath;
        }
        if (log.isLoggable(Level.FINE)) {
            log.fine("ORYGINALPATH:[" + oryginalPath + "]");
            log.fine("APPLICATIONPREFIX:[" + applicationPrefix + "]");
            log.fine("STANDARDJSPPREFIX:[" + standardJspPrefix + "]");
            log.fine("REDIRECTING:[" + ret + "]");
        }
        ((HttpServletResponse) resp).sendRedirect(ret);
        return true;
    }

    @Override
    public void destroy() {
        //add code to release any resource
    }

    public FilterConfig getFilterConfig() {
        return filterConfig;
    }
}
